﻿using System;
using System.Collections.Generic;
using MxNetwork;
using System.Net.Sockets;
using System.Threading;

namespace MxNetwork
{
    public class MxNwkChannelDesc
    {
        public enum ChannelProcessState
        {
            CPS_UNINIT,					// 通道尚未初始化
            CPS_EXCHANGE_ENCRYPE_KEY,	// 通道在交换加密密钥（安全协议使用）
            CPS_HASINIT,				// 通道完成数据操作
            PS_MAX
        }

        public MxNwkChannelDesc()
        {
            hChannel = -1;
            hListener = -1;
            IsEncrypt = false;
            LocalType = 0;
            LocalID = 0;
            RemoteType = 0;
            RemoteID = 0;
            IsClient = false;
            CreateTimeSec = 0;
            State = (int)ChannelProcessState.CPS_UNINIT;
            ConnectorID = null;
        }

        public MxNwkChannelDesc(MxNwkChannelDesc t)
        {
            Local = t.Local;
            Remote = t.Remote;
            SizeRecvBuffer = t.SizeRecvBuffer;
            hChannel = t.hChannel;
            hListener = t.hListener;
            IsEncrypt = t.IsEncrypt;
            LocalType = t.LocalType;
            LocalID = t.LocalID;
            RemoteType = t.RemoteType;
            RemoteID = t.RemoteID;
            IsClient = t.IsClient;
            CreateTimeSec = t.CreateTimeSec;
            State = t.State;
            ConnectorID = null;
        }

        public MxNwkAddressIP Local;	// 本地IP
        public MxNwkAddressIP Remote;	// 远端IP

        public UInt32 SizeRecvBuffer;
        public int hChannel;	    // 通道ID（全局唯一）
        public int hListener;	    // 监听端口（为null时，表示由connecter产生的通道，而非Accepte产生的网络通道）
        public bool IsEncrypt;	    // 是否加密，true 加密 false 不加密
        public UInt32 LocalType;	// 本地类型
        public UInt32 LocalID;		// 本地ID
        public UInt32 RemoteType;	// 远端类型
        public UInt32 RemoteID;	// 远端ID
        public MxNwkConnecter ConnectorID;	// 连接器的ID（通过连接创建的通道才有）
        public bool IsClient;	    // true 是客户端通道， false 是服务端通道
        public int CreateTimeSec; // 连接创建的时间
        public int State;	// 通道当前状态	
        public Socket Sock;
    }

    public class MxNwkChannel : IDisposable
    {
        public enum OprateType
        {
            OTIO_POSTED_RECV,
            OTIO_POSTED_SEND,
        };

        public enum ProcessState
        {
            PS_PROCESSING,			// 工作线程正在处理中
            PS_COMPLETION,			// 工作线程完成数据操作
            PS_MAX
        };

        internal class AsyncState
        {
            public readonly byte[] buffer;
            public readonly int offset;
            public readonly int size;
            public readonly MxNwkChannel channel;

            public AsyncState(MxNwkChannel channel)
            {
                this.channel = channel;
            }

            public AsyncState(byte[] buffer, int offset, int size, MxNwkChannel channel)
            {
                this.buffer = buffer;
                this.offset = offset;
                this.size = size;
                this.channel = channel;
            }
        }

        private void PostRecv(MxNwkChannel channel)
        {
            try
            {
                byte[] buffer = new byte[10240];
                channel.m_pDesc.Sock.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, onReceived, new AsyncState(buffer, 0, buffer.Length, channel));                
            }            
            catch (SocketException)
            {
                MxNwkChannel tmp = MxNetworkManager.GetInstance().RemoveChannel(channel.m_pDesc.hChannel);
                if (tmp != null)
                {
                    tmp.Dispose();
                }
            }          
            catch (ObjectDisposedException)
            {
                return;
            }
        }

        private void PostSend(byte[] buffer, int offset, int size, MxNwkChannel channel)
        {
            try
            {
                channel.m_pDesc.Sock.BeginSend(buffer, offset, size, SocketFlags.None, onSend, new AsyncState(buffer, offset, size, channel));
            }            
            catch (SocketException)
            {
                MxNwkChannel tmp = MxNetworkManager.GetInstance().RemoveChannel(channel.m_pDesc.hChannel);
                if (tmp != null)
                {
                    tmp.Dispose();
                }
            }    
            catch (ObjectDisposedException)
            {
                return;
            }
        }
        
        public MxNwkChannel()
        {
            onReceived = new AsyncCallback(ReportRecvDataComplete);
            onSend = new AsyncCallback(ReportSendDataComplete);
        }

        public bool Create(MxNwkChannelDesc pDesc, MxNwkObsChannel pObs)
        {
            m_pDesc = pDesc;
            m_pObserver = pObs;
            return true;
        }

        public bool SendMsg(MxNwkPacket pMsg)
        {            
            return SendPacket(pMsg);
        }

	    public bool SendRequest(MxNwkPacket pRequest, MxToken pAct, int overtimeSec)
        {
            pRequest.SetPacketToken(MxNetworkManager.GetInstance().GenRequestTokenID());	        
            MxNetworkManager.GetInstance().AddSendRequest(pRequest, pAct);
	        return SendPacket(pRequest);
        }        

	    public bool SendAck(MxNwkPacket pAck)
        {            
	        return SendPacket(pAck);
        }

	    public bool SendPacket(MxNwkPacket pPkt)
        {            
            pPkt.SetPacketRealSize();
            PostSend(pPkt.GetData(), 0, (int)pPkt.GetRealSize(), this);
            return true;
        }

	    public bool CallOnRecvPacket(int hChannel, MxNwkPacket pPkt)
        {            
            return true;
        }
        
        public void Dispose()
        {
            if (m_pDesc.Sock != null)
            {
                m_pDesc.Sock.Close();
                m_pDesc.Sock = null;
                m_pObserver.OnDisConnected(m_pDesc.hChannel, 0, m_pDesc.LocalType, m_pDesc.LocalID, m_pDesc.RemoteType, m_pDesc.RemoteID, m_pDesc.Remote.GetIP());
            }
        }

        private void ReportRecvDataComplete(IAsyncResult asyncResult)	// IOCP工作线程收到数据之后，调用该方法(非用户调用)
        {
            AsyncState state = asyncResult.AsyncState as AsyncState;
            try
            {                
                if(state.channel.m_pDesc.Sock != null)
                {
                    int recvSize = state.channel.m_pDesc.Sock.EndReceive(asyncResult);

					//Console.Write(recvSize.ToString());

                    if (recvSize > 0)
                    {
                        m_cachePacket.WriteData(state.buffer, recvSize);

                        while (m_cachePacket.GetWriteOffset() - m_cachePacket.GetReadOffset() > sizeof(uint))
                        {
                            uint sz = m_cachePacket.ReadUInt32ByPos(m_cachePacket.GetReadOffset());

                            if (sz > m_cachePacket.GetWriteOffset() - m_cachePacket.GetReadOffset())
                            {
                                break;
                            }

                            MxNwkPacket tmpPacket = new MxNwkPacket();
                            tmpPacket.WriteData(m_cachePacket.GetData(), m_cachePacket.GetReadOffset(), (int)sz);
                            tmpPacket.SetPacketChannelID(m_pDesc.hChannel);
                            tmpPacket.SetReadOffset((int)tmpPacket.GetDataPos());
                            m_cachePacket.SetReadOffset(m_cachePacket.GetReadOffset() + (int)sz);

                            if ((MxNwkChannelDesc.ChannelProcessState)m_pDesc.State == MxNwkChannelDesc.ChannelProcessState.CPS_UNINIT)
                            {// 如果通道尚未初始化

                                uint id = tmpPacket.GetPacketID();
                                if (id == 1)
                                {
                                    if (m_pDesc.IsClient)
                                    {
                                        Interlocked.Exchange(ref m_pDesc.State, (int)MxNwkChannelDesc.ChannelProcessState.CPS_HASINIT);

                                        //MxEngineLog::Instance()->Log(MX_LOG_NORMAL, L" Client receive init packet success, RemoteIP:%s RemoteType:%d RemoteID:%d !", m_pDesc->Remote.GetIPStr().c_str(), m_pDesc->RemoteType, m_pDesc->RemoteID);
                                    }
                                    else
                                    {
                                        m_pDesc.RemoteType = tmpPacket.ReadUInt32();
                                        m_pDesc.RemoteID = tmpPacket.ReadUInt32();

                                        Interlocked.Exchange(ref m_pDesc.State, (int)MxNwkChannelDesc.ChannelProcessState.CPS_HASINIT);

                                        {
                                            MxNwkPacket pPkt = new MxNwkPacket(MxNwkPacket.MxNwkPacketType.NPT_MESSAGE);
                                            pPkt.SetPacketID(1);	// 响应客户端通报本地类型和本地ID
                                            pPkt.Write((uint)0);
                                            SendMsg(pPkt);
                                        }

                                        //MxEngineLog::Instance()->Log(MX_LOG_NORMAL, L" Server receive init packet success, RemoteIP:%s RemoteType:%d RemoteID:%d !", m_pDesc->Remote.GetIPStr().c_str(), m_pDesc->RemoteType, m_pDesc->RemoteID);
                                    }

                                    MxNetworkManager.GetInstance().RemoveTmpChannel(this);
                                    MxNetworkManager.GetInstance().AddChannel(this);
                                    m_pObserver.OnConnected(m_pDesc.hChannel, m_pDesc.LocalType, m_pDesc.LocalID, m_pDesc.RemoteType, m_pDesc.RemoteID, m_pDesc.Remote.GetIP());
                                    FirstRecv(); // 确保在OnConnected之后才继续接收数据包

                                    m_cachePacket.SetReadOffset(0);
                                    m_cachePacket.SetWriteOffset(0);

                                    return;	// 直接返回，并不投递Recv请求，直到执行完OnConnected之后，再继续投递Recv
                                }
                                else
                                {
                                    //MxEngineLog::Instance()->Log(MX_LOG_ERROR, L" Server/Client receive unknown init packet, RemoteIP:%s RemoteType:%d RemoteID:%d PacketID:%d !", id, m_pDesc->RemoteType, m_pDesc->RemoteID, m_pDesc->Remote.GetIPStr().c_str());
                                    return;	// 如果第一个包不是初始化包，直接跳出循环，也不投递新的Recv请求，等待临时通道过期被自动清除
                                }
                            }
                            else
                            {
                                // 激发获取到的包		
                                if (m_pObserver != null)
                                {
                                    tmpPacket.SetRemoteType(m_pDesc.RemoteType);
                                    tmpPacket.SetRemoteID(m_pDesc.RemoteID);
                                    tmpPacket.SetRemoteIP(m_pDesc.Remote);
                                    m_pObserver.OnRecvPacket(m_pDesc.hChannel, tmpPacket);
                                }
                            }
                        }

                        if (m_cachePacket.GetReadOffset() < m_cachePacket.GetWriteOffset())
                        {
                            MxNwkPacket newCache = new MxNwkPacket();
                            newCache.WriteData(m_cachePacket.GetData(), m_cachePacket.GetReadOffset(), m_cachePacket.GetWriteOffset() - m_cachePacket.GetReadOffset());
                            m_cachePacket = newCache;
                        }
                        else
                        {
                            m_cachePacket.SetReadOffset(0);
                            m_cachePacket.SetWriteOffset(0);
                        }

                        PostRecv(this);
                    }
                    else
                    {
                        MxNwkChannel tmp = MxNetworkManager.GetInstance().RemoveChannel(state.channel.m_pDesc.hChannel);
                        if (tmp != null)
                        {
                            tmp.Dispose();
                        }
                    }                    
                }
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException)
            {
                MxNwkChannel tmp = MxNetworkManager.GetInstance().RemoveChannel(state.channel.m_pDesc.hChannel);
                if (tmp != null)
                {
                    tmp.Dispose();
                }
            }              
        }

        private void ReportSendDataComplete(IAsyncResult asyncResult)	// IOCP工作线程发送数据之后，调用该方法(非用户调用)
        {
            AsyncState state = asyncResult.AsyncState as AsyncState;
            try
            {                
                if(state.channel.m_pDesc.Sock != null)
                {
                    int sentBytes = state.channel.m_pDesc.Sock.EndSend(asyncResult);
                }                
            }            
            catch (SocketException)
            {
                MxNwkChannel tmp = MxNetworkManager.GetInstance().RemoveChannel(state.channel.m_pDesc.hChannel);
                if(tmp != null)
                {
                    tmp.Dispose();
                }
            }
            catch (ObjectDisposedException)
            {
                return;
            }
        }

	    private void ReportDisconnected()		// IOCP感知断连之后，调用该方法，由通道决定处理断连的方式(非用户调用)
        {
            PostRecv(this);
        }

        public MxNwkChannelDesc GetChannelDesc()
        {
            return m_pDesc;
        }

        public MxNwkObsChannel GetChannelObserver()
        {
            return m_pObserver;
        }

        public bool FirstRecv()        		// 发送一次Recv请求，非多线程安全
        {
            PostRecv(this);
            return true;
        }

        private readonly AsyncCallback onReceived;
        private readonly AsyncCallback onSend;

        private MxNwkObsChannel m_pObserver;
        public MxNwkChannelDesc m_pDesc;

        MxNwkPacket m_cachePacket = new MxNwkPacket();        
    }
}
